package com.yindian.cos.easy.security.config;

import com.yindian.cos.easy.security.authentication.AbstractAuthentication;
import com.yindian.cos.easy.security.authentication.AuthenticationChain;
import com.yindian.cos.easy.security.authentication.login.normal.form.FormLoginLoginAuthentication;
import com.yindian.cos.easy.security.authentication.login.normal.form.FormLoginUserService;
import com.yindian.cos.easy.security.authentication.login.normal.smsCode.SmsCodeLoginLoginAuthentication;
import com.yindian.cos.easy.security.authentication.login.normal.smsCode.SmsCodeLoginUserService;
import com.yindian.cos.easy.security.authentication.login.social.SocialUserService;
import com.yindian.cos.easy.security.authentication.login.social.core.*;
import com.yindian.cos.easy.security.authentication.login.social.qq.QqAccessTokenFactory;
import com.yindian.cos.easy.security.authentication.login.social.qq.QqLoginAuthentication;
import com.yindian.cos.easy.security.authentication.login.social.qq.QqUserInfoFactory;
import com.yindian.cos.easy.security.authentication.login.social.weibo.WeiBoAccessTokenFactory;
import com.yindian.cos.easy.security.authentication.login.social.weibo.WeiBoLoginAuthentication;
import com.yindian.cos.easy.security.authentication.login.social.weibo.WeiBoUserInfoFactory;
import com.yindian.cos.easy.security.authentication.login.social.weixin.WeiXinAccessTokenFactory;
import com.yindian.cos.easy.security.authentication.login.social.weixin.WeiXinLoginAuthentication;
import com.yindian.cos.easy.security.authentication.login.social.weixin.WeiXinUserInfoFactory;
import com.yindian.cos.easy.security.authentication.logout.LogoutAuthentication;
import com.yindian.cos.easy.security.authentication.logout.LogoutService;
import com.yindian.cos.easy.security.authentication.token.AbstractTokenFactory;
import com.yindian.cos.easy.security.authentication.token.JwtTokenFactory;
import com.yindian.cos.easy.security.authentication.token.RedisTokenStore;
import com.yindian.cos.easy.security.authentication.token.TokenStore;
import com.yindian.cos.easy.security.authentication.verify.BasicAuthVerifyAuthentication;
import com.yindian.cos.easy.security.authentication.verify.SystemPathVerifyAuthentication;
import com.yindian.cos.easy.security.authentication.verify.TokenVerifyAuthentication;
import com.yindian.cos.easy.security.filter.DispatchFilter;
import com.yindian.cos.easy.security.handler.AuthenticationFailureHandler;
import com.yindian.cos.easy.security.handler.AuthenticationSuccessHandler;
import com.yindian.cos.easy.security.handler.LoginSuccessHandler;
import com.yindian.cos.easy.security.properties.EasySecurityProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.client.RestTemplate;

import javax.sql.DataSource;
import java.util.Map;

/**
 * easy security自动配置
 *
 * @author zhangchuanhao
 * @create 2018/11/18
 */
@Order
@Configuration
@ConditionalOnProperty(prefix = "easy.security.authorization", name = "enable", havingValue = "true")
public class EasySecurityAutoConfig {

    @Bean
    @ConditionalOnMissingBean(value = RestTemplate.class)
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    /**
     * 注册过滤器入口
     */
    @Bean
    public FilterRegistrationBean dispatchFilter(AuthenticationChain authenticationChain,
                                                 @Autowired(required = false) AuthenticationFailureHandler authenticationFailureHandler,
                                                 @Autowired(required = false) LoginSuccessHandler loginSuccessHandler,
                                                 @Autowired(required = false) AuthenticationSuccessHandler authenticationSuccessHandler) {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new DispatchFilter(authenticationChain, authenticationFailureHandler, loginSuccessHandler, authenticationSuccessHandler));
        registration.addUrlPatterns("/*");
        registration.setName("dispatchFilter");
        registration.setOrder(-9999);
        registration.setEnabled(true);
        return registration;
    }

    /**
     * 注册拦截器链
     */
    @Bean
    public AuthenticationChain authenticationChain(Map<String, AbstractAuthentication> authenticationMap) {
        return new AuthenticationChain(authenticationMap);
    }

    /**
     * 启用basic auth验证
     */
    @Bean
    @ConditionalOnProperty(prefix = "easy.security.authorization.basic-auth", name = "enable", havingValue = "true")
    public AbstractAuthentication basicAuthVerifyAuthentication(EasySecurityProperties easySecurityProperties) {
        return new BasicAuthVerifyAuthentication(easySecurityProperties);
    }

    /**
     * 启用表单登录
     */
    @Bean
    @ConditionalOnProperty(prefix = "easy.security.authorization.form", name = "enable", havingValue = "true")
    public AbstractAuthentication formLoginAuthentication(EasySecurityProperties easySecurityProperties,
                                                          FormLoginUserService formLoginUserService) {
        return new FormLoginLoginAuthentication(easySecurityProperties, formLoginUserService);
    }

    /**
     * 启用短信验证码登录
     */
    @Bean
    @ConditionalOnProperty(prefix = "easy.security.authorization.sms-code", name = "enable", havingValue = "true")
    public AbstractAuthentication smsCodeLoginAuthentication(EasySecurityProperties easySecurityProperties,
                                                             SmsCodeLoginUserService smsCodeLoginUserService) {
        return new SmsCodeLoginLoginAuthentication(easySecurityProperties, smsCodeLoginUserService);
    }

    /**
     * 启用qq登录
     */
    @Configuration
    @AutoConfigureAfter(value = EasySecurityAutoConfig.class)
    @ConditionalOnProperty(prefix = "easy.security.authorization", name = "enable", havingValue = "true")
    public static class QqAutoConfig {
        @Bean
        @ConditionalOnProperty(prefix = "easy.security.authorization.qq", name = "enable", havingValue = "true")
        public AbstractAuthentication qqLoginAuthentication(EasySecurityProperties easySecurityProperties) {
            return new QqLoginAuthentication(easySecurityProperties);
        }

        @Bean
        @ConditionalOnMissingBean(name = "qqAccessTokenFactory")
        @ConditionalOnProperty(prefix = "easy.security.authorization.qq", name = "enable", havingValue = "true")
        public AbstractAccessTokenFactory qqAccessTokenFactory(EasySecurityProperties easySecurityProperties, RestTemplate restTemplate) {
            return new QqAccessTokenFactory(easySecurityProperties, restTemplate);
        }

        @Bean
        @ConditionalOnMissingBean(name = "qqUserInfoFactory")
        @ConditionalOnProperty(prefix = "easy.security.authorization.qq", name = "enable", havingValue = "true")
        public AbstractUserInfoFactory qqUserInfoFactory(EasySecurityProperties easySecurityProperties, RestTemplate restTemplate) {
            return new QqUserInfoFactory(easySecurityProperties, restTemplate);
        }
    }

    /**
     * 启用新浪微博登录
     */
    @Configuration
    @AutoConfigureAfter(value = EasySecurityAutoConfig.class)
    @ConditionalOnProperty(prefix = "easy.security.authorization", name = "enable", havingValue = "true")
    public static class WeiBoAutoConfig {
        @Bean
        @ConditionalOnProperty(prefix = "easy.security.authorization.weibo", name = "enable", havingValue = "true")
        public AbstractAuthentication weiBoLoginAuthentication(EasySecurityProperties easySecurityProperties) {
            return new WeiBoLoginAuthentication(easySecurityProperties);
        }

        @Bean
        @ConditionalOnMissingBean(name = "weiBoAccessTokenFactory")
        @ConditionalOnProperty(prefix = "easy.security.authorization.weibo", name = "enable", havingValue = "true")
        public AbstractAccessTokenFactory weiBoAccessTokenFactory(EasySecurityProperties easySecurityProperties, RestTemplate restTemplate) {
            return new WeiBoAccessTokenFactory(easySecurityProperties, restTemplate);
        }

        @Bean
        @ConditionalOnMissingBean(name = "weiBoUserInfoFactory")
        @ConditionalOnProperty(prefix = "easy.security.authorization.weibo", name = "enable", havingValue = "true")
        public AbstractUserInfoFactory weiBoUserInfoFactory(EasySecurityProperties easySecurityProperties, RestTemplate restTemplate) {
            return new WeiBoUserInfoFactory(easySecurityProperties, restTemplate);
        }
    }

    /**
     * 启用微信登录
     */
    @Configuration
    @AutoConfigureAfter(value = EasySecurityAutoConfig.class)
    @ConditionalOnProperty(prefix = "easy.security.authorization", name = "enable", havingValue = "true")
    public static class WeiXinAutoConfig {
        @Bean
        @ConditionalOnProperty(prefix = "easy.security.authorization.weixin", name = "enable", havingValue = "true")
        public AbstractAuthentication weiXinLoginAuthentication(EasySecurityProperties easySecurityProperties) {
            return new WeiXinLoginAuthentication(easySecurityProperties);
        }

        @Bean
        @ConditionalOnMissingBean(name = "weiXinAccessTokenFactory")
        @ConditionalOnProperty(prefix = "easy.security.authorization.weixin", name = "enable", havingValue = "true")
        public AbstractAccessTokenFactory weiXinAccessTokenFactory(EasySecurityProperties easySecurityProperties, RestTemplate restTemplate) {
            return new WeiXinAccessTokenFactory(easySecurityProperties, restTemplate);
        }

        @Bean
        @ConditionalOnMissingBean(name = "weiXinUserInfoFactory")
        @ConditionalOnProperty(prefix = "easy.security.authorization.weixin", name = "enable", havingValue = "true")
        public AbstractUserInfoFactory weiXinUserInfoFactory(EasySecurityProperties easySecurityProperties, RestTemplate restTemplate) {
            return new WeiXinUserInfoFactory(easySecurityProperties, restTemplate);
        }
    }

    /**
     * 是否启用退出拦截器
     */
    @Bean
    @ConditionalOnProperty(prefix = "easy.security.authorization.logout", name = "enable", havingValue = "true")
    public AbstractAuthentication logoutAuthentication(EasySecurityProperties easySecurityProperties, LogoutService logoutService) {
        return new LogoutAuthentication(logoutService, easySecurityProperties.getLogout());
    }

    /**
     * 是否启用默认的mysql策略存储第三方用户
     */
    @Bean
    @ConditionalOnProperty(prefix = "easy.security.authorization", name = "mysql-user-info-store-enable", havingValue = "true")
    @ConditionalOnMissingBean(value = UserInfo.class)
    public UserInfoStore userInfoStore(DataSource dataSource, EasySecurityProperties easySecurityProperties) {
        return new MysqlUserInfoStore(dataSource, easySecurityProperties);
    }

    /**
     * 第三方登录获取用户信息成功后的后置处理器
     */
    @Bean
    @ConditionalOnProperty(prefix = "easy.security.authorization", name = "default-post-processor-enable", havingValue = "true")
    @ConditionalOnMissingBean(value = AbstractPostProcessor.class)
    public AbstractPostProcessor postProcessor(EasySecurityProperties easySecurityProperties,
                                               UserInfoStore userInfoStore,
                                               LoginSuccessHandler loginSuccessHandler,
                                               SocialUserService socialUserService) {
        return new DefaultPostProcessor(easySecurityProperties, userInfoStore, loginSuccessHandler, socialUserService);
    }

    /**
     * 是否启用默认的jwt生成策略
     */
    @Bean
    @ConditionalOnProperty(prefix = "easy.security.authorization", name = "jwt-token-factory-enable", havingValue = "true")
    @ConditionalOnMissingBean(value = AbstractTokenFactory.class)
    public AbstractTokenFactory tokenFactory(TokenStore tokenStore, EasySecurityProperties easySecurityProperties) {
        return new JwtTokenFactory(tokenStore, easySecurityProperties.getToken());
    }

    /**
     * 是否启用默认的redis策略存储token
     */
    @Bean
    @ConditionalOnProperty(prefix = "easy.security.authorization", name = "redis-token-store-enable", havingValue = "true")
    @ConditionalOnMissingBean(value = TokenStore.class)
    public TokenStore tokenStore(RedisConnectionFactory redisConnectionFactory,
                                 RedisTemplate redisTemplate,
                                 EasySecurityProperties easySecurityProperties) {
        try {
            if (!redisConnectionFactory.getConnection().isClosed()) {
                return new RedisTokenStore(redisTemplate, easySecurityProperties.getToken());
            }
        } catch (Exception ex) {
        }
        throw new RuntimeException("Please configure your redis");
    }

    /**
     * 是否启用token拦截器
     */
    @Bean
    @ConditionalOnProperty(prefix = "easy.security.authorization.token-verify", name = "enable", havingValue = "true")
    public AbstractAuthentication tokenVerifyAuthentication(EasySecurityProperties easySecurityProperties, TokenStore tokenStore) {
        return new TokenVerifyAuthentication(easySecurityProperties, tokenStore);
    }

    /**
     * 根据系统配置自动放行某些url
     */
    @Bean
    public AbstractAuthentication systemPathVerifyAuthentication(EasySecurityProperties easySecurityProperties) {
        return new SystemPathVerifyAuthentication(easySecurityProperties);
    }

}
